package com.bizmda.bizsip.app.service;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.bizmda.bizsip.app.checkrule.*;
import com.bizmda.bizsip.app.config.AppServiceMapping;
import com.bizmda.bizsip.app.config.CheckRuleConfigMapping;
import com.bizmda.bizsip.app.config.ControlRuleConfigMapping;
import com.bizmda.bizsip.app.controlrule.ControlRule;
import com.bizmda.bizsip.app.controlrule.ControlRuleConfig;
import com.bizmda.bizsip.app.executor.AbstractAppExecutor;
import com.bizmda.bizsip.app.executor.script.MagicScriptHelper;
import com.bizmda.bizsip.common.*;
import com.bizmda.bizsip.service.AppLogService;
import com.bizmda.log.trace.MDCTraceUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.ssssssss.script.MagicScriptContext;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: 史正烨
 * @date: 2022/3/1 4:04 下午
 * @Description:
 */
@Service
@Slf4j
public class AppService {
    private static final String CONTROL_ACTION_ERROR = "error";
    private static final String FIELD_MESSAGE = "message";
    private static final String FIELD_REQUEST = "request";
    private static final String FIELD_RULES = "rules";
    private static final String FIELD_CONTROL = "control";
    @Autowired
    private AppServiceMapping appServiceMapping;
    @Autowired
    private CheckRuleConfigMapping checkRuleConfigMapping;
    @Autowired
    private ControlRuleConfigMapping controlRuleConfigMapping;
    @Autowired
    private AppLogService appLogService;

    public BizMessage<JSONObject> process(String appServiceId, String traceId, JSONObject inJsonObject) {
        log.debug("开始处理App服务: {}", appServiceId);
        log.trace("App服务[{}]请求报文:\n{}", appServiceId, BizUtils.buildJsonLog(inJsonObject));

        BizMessage<JSONObject> bizMessage = BizMessage.createNewTransaction(appServiceId);
        if (!CharSequenceUtil.isEmpty(traceId)) {
            log.debug("设置BizMessage.traceId:{}",traceId);
            bizMessage.setTraceId(traceId);
        }
        bizMessage.setData(inJsonObject);
        BizMessage<JSONObject> inMessage = BizTools.copyBizMessage(bizMessage);
        BizTools.bizMessageThreadLocal.set(bizMessage);
        BizTools.delayAppServiceContextThreadLocal.set(new DelayAppServiceContext());
        BizTools.serviceIdThreadLocal.set(appServiceId);

        BizMessage<JSONObject> outMessage;

        try {
            JSONArray jsonArray = this.checkFieldRule(appServiceId, inJsonObject);
            if (!jsonArray.isEmpty()) {
                throw new BizException(BizResultEnum.CHECKRULE_FIELD_CHECK_ERROR, jsonArray.toString());
            }

            jsonArray = this.checkServiceRule(appServiceId, inJsonObject);
            if (!jsonArray.isEmpty()) {
                throw new BizException(BizResultEnum.CHECKRULE_SERVICE_CHECK_ERROR, jsonArray.toString());
            }

            ControlRuleConfig controlRuleConfig = this.controlRuleConfigMapping.getControlRuleConfig(appServiceId);
            if (controlRuleConfig != null) {
                inJsonObject = this.doControlRule(controlRuleConfig, inJsonObject);
            }

            AbstractAppExecutor appExecutor = this.appServiceMapping.getAppExecutor(appServiceId);
            if (appExecutor == null) {
                log.error("App服务[{}]不存在", appServiceId);
                throw new BizException(BizResultEnum.APP_SERVICE_NOT_FOUND,
                        StrFormatter.format("App服务[{}]不存在", appServiceId));
            }


            log.debug("调用App服务[{}]: traceId={}", appServiceId, bizMessage.getTraceId());
            outMessage = appExecutor.doAppService(bizMessage);
            if (controlRuleConfig != null) {
                outMessage = this.updateControlRule(controlRuleConfig, inJsonObject, outMessage);
            }
        } catch (BizException e) {
            log.warn("App服务执行出错:{}-{}\n{}", e.getCode(),e.getMessage(),e.getExtMessage());
            outMessage = BizMessage.buildFailMessage(bizMessage, e);
        } finally {
            BizTools.delayAppServiceContextThreadLocal.remove();
            BizTools.bizMessageThreadLocal.remove();
            MDCTraceUtils.removeTraceId();
            BizTools.serviceIdThreadLocal.remove();
            BizTools.controlRuleThreadLocal.remove();
        }
        if (outMessage.getCode() == 0) {
            log.debug("发送交易成功日志");
            this.appLogService.sendAppSuccessLog(inMessage, outMessage);
        } else {
            log.debug("发送交易失败日志");
            this.appLogService.sendAppFailLog(inMessage, outMessage);
        }
        return outMessage;
    }

    private JSONArray checkFieldRule(String serviceId, JSONObject message) throws BizException {
        JSONArray jsonArray = new JSONArray();
        CheckRuleConfig checkRuleConfig = this.checkRuleConfigMapping.getCheckRuleConfig(serviceId);
        if (checkRuleConfig == null) {
            return jsonArray;
        }
        List<FieldCheckRule> fieldCheckRuleList = checkRuleConfig.getFieldCheckRuleList();
        if (fieldCheckRuleList == null) {
            return jsonArray;
        }
        log.debug("开始App服务[{}]域级规则校验", serviceId);

        List<FieldChcekRuleResult> fieldChcekRuleResultList = FieldCheckRuleHelper.checkFieldRule(message, fieldCheckRuleList, checkRuleConfig.getFieldCheckMode());

        for (FieldChcekRuleResult fieldChcekRuleResult : fieldChcekRuleResultList) {
            log.warn("域级校验规则不通过:{}-{}", fieldChcekRuleResult.getField(),
                    fieldChcekRuleResult.getMessage());
            JSONObject jsonObject = new JSONObject();
            jsonObject.set("field", fieldChcekRuleResult.getField());
            jsonObject.set(FIELD_MESSAGE, fieldChcekRuleResult.getMessage());
            jsonArray.add(jsonObject);
        }
        return jsonArray;
    }

    private JSONArray checkServiceRule(String serviceId, JSONObject message) throws BizException {
        JSONArray jsonArray = new JSONArray();
        CheckRuleConfig checkRuleConfig = this.checkRuleConfigMapping.getCheckRuleConfig(serviceId);
        if (checkRuleConfig == null) {
            return jsonArray;
        }
        List<ServiceCheckRule> serviceCheckRuleList = checkRuleConfig.getServiceCheckRuleList();
        if (serviceCheckRuleList == null) {
            return jsonArray;
        }
        log.debug("开始App服务[{}]服务级规则校验", serviceId);

        List<ServiceChcekRuleResult> serviceChcekRuleResultList = ServiceCheckRuleHelper.checkServiceRule(message, serviceCheckRuleList, checkRuleConfig.getFieldCheckMode());

        for (ServiceChcekRuleResult serviceChcekRuleResult : serviceChcekRuleResultList) {
            log.warn("服务级校验规则不通过:{}", serviceChcekRuleResult.getResult());
            JSONObject jsonObject = new JSONObject();
            jsonObject.set(FIELD_MESSAGE, serviceChcekRuleResult.getResult());
            jsonArray.add(jsonObject);
        }
        return jsonArray;
    }

    private JSONObject doControlRule(ControlRuleConfig controlRuleConfig, JSONObject inJsonObject) throws BizException {
        MagicScriptContext context = new MagicScriptContext();
        context.set(FIELD_REQUEST, inJsonObject);
        if (!CharSequenceUtil.isBlank(controlRuleConfig.getPreRatingScript())) {
            log.debug("执行风控规则pre-rating-script脚本");
            log.trace("pre-rating-script脚本:\n" + controlRuleConfig.getPreRatingScript());
            MagicScriptHelper.executeScript(controlRuleConfig.getPreRatingScript(), context);
        }

        List<ControlRule> controlRuleList = controlRuleConfig.getControlRuleList();
        JSONObject controlJsonObject = BizTools.controlRuleThreadLocal.get();
        if (controlJsonObject == null) {
            controlJsonObject = new JSONObject();
        }
        JSONArray jsonArray = new JSONArray();
        if (controlRuleList != null && (!controlRuleList.isEmpty())) {
            log.debug("执行风控规则rules");
            List<ControlRuleResult> controlRuleResultList = ControlRuleHelper.controlRule(inJsonObject, controlRuleList);
            jsonArray = JSONUtil.parseArray(controlRuleResultList);
        }
        context.set(FIELD_RULES, jsonArray);

        context.set(FIELD_CONTROL, controlJsonObject);

        if (!CharSequenceUtil.isBlank(controlRuleConfig.getRatingScript())) {
            log.debug("执行风控规则rating-script脚本");
            log.trace("rating-script脚本:\n" + controlRuleConfig.getRatingScript());
            MagicScriptHelper.executeScript(controlRuleConfig.getRatingScript(), context);
        }
        Map<String, Object> map = context.getRootVariables();
        JSONObject jsonObject = (JSONObject) map.get(FIELD_REQUEST);
        log.trace("rating-script脚本执行后request:\n{}", BizUtils.buildJsonLog(jsonObject));
        controlJsonObject = (JSONObject) map.get(FIELD_CONTROL);
        String action = controlJsonObject.getStr("action");
        if (CONTROL_ACTION_ERROR.equalsIgnoreCase(action)) {
            String message = controlJsonObject.getStr(FIELD_MESSAGE);
            log.warn("风控规则检查不通过:{}", message);
            throw new BizException(BizResultEnum.CONTROL_RULE_NO_PASS, message);
        }
        JSONArray jsonArray1 = controlJsonObject.getJSONArray(FIELD_RULES);
        if (jsonArray1 == null) {
            controlJsonObject.set(FIELD_RULES, jsonArray);
        }
        else {
            jsonArray1.addAll(jsonArray);
            controlJsonObject.set(FIELD_RULES,jsonArray1);
        }
        log.trace("rating-script脚本执行后control:\n{}", BizUtils.buildJsonLog(controlJsonObject));
        BizTools.controlRuleThreadLocal.set(controlJsonObject);
        List<String> subControlRuleList = controlRuleConfig.getSubControlRules();
        if (subControlRuleList == null) {
            return jsonObject;
        }
        for(String subControlRule:subControlRuleList) {
            Map<String,Object> map1 = new HashMap<>();
            map1.put(FIELD_REQUEST,jsonObject);
            String subControlRuleString = BizTools.getElStringResult(subControlRule,map1);
            log.debug("执行子风控规则sub-control-rule:{},{}",subControlRule,subControlRuleString);
            ControlRuleConfig subControlRuleConfig = this.controlRuleConfigMapping.getControlRuleConfig(subControlRuleString);
            if (subControlRuleConfig == null) {
                log.warn("子风控规则[{}]没有找到:{}",subControlRule,subControlRuleString);
            }
            else {
                inJsonObject = this.doControlRule(subControlRuleConfig, inJsonObject);
            }
        }
        return inJsonObject;
    }

    private BizMessage<JSONObject> updateControlRule(ControlRuleConfig controlRuleConfig, JSONObject inJsonObject, BizMessage<JSONObject> outBizMessage) throws BizException {
        if (CharSequenceUtil.isBlank(controlRuleConfig.getUpdatingScript())) {
            return outBizMessage;
        }
        JSONObject controlJsonObject = BizTools.controlRuleThreadLocal.get();
        boolean updatingFlag = controlJsonObject.getBool(BizConstant.CONTROL_RULE_UPDATING_FLAG, true);
        if (!updatingFlag) {
            return outBizMessage;
        }
        log.debug("执行风控规则updating-script脚本");
        log.trace("updating-script脚本:\n" + controlRuleConfig.getUpdatingScript());
        MagicScriptContext context = new MagicScriptContext();
        context.set(FIELD_REQUEST, inJsonObject);
        context.set("response", outBizMessage);
        context.set(FIELD_CONTROL, controlJsonObject);
        MagicScriptHelper.executeScript(controlRuleConfig.getUpdatingScript(), context);
        Map<String, Object> map = context.getRootVariables();
        outBizMessage = (BizMessage<JSONObject>) map.get("response");
        log.trace("执行风控规则updating-script脚本后，返回response:\n{}", BizUtils.buildBizMessageLog(outBizMessage));
        return outBizMessage;
    }
}
